diff options
Diffstat (limited to 'app/[lng]/engineering/(engineering)')
27 files changed, 0 insertions, 1706 deletions
diff --git a/app/[lng]/engineering/(engineering)/docu-list-rule/code-groups/page.tsx b/app/[lng]/engineering/(engineering)/docu-list-rule/code-groups/page.tsx deleted file mode 100644 index 5aebf15d..00000000 --- a/app/[lng]/engineering/(engineering)/docu-list-rule/code-groups/page.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import * as React from "react"; -import { type SearchParams } from "@/types/table"; -import { Shell } from "@/components/shell"; -import { Skeleton } from "@/components/ui/skeleton"; -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"; -import { getCodeGroups } from "@/lib/docu-list-rule/code-groups/service"; -import { CodeGroupsTable } from "@/lib/docu-list-rule/code-groups/table/code-groups-table"; -import { searchParamsCodeGroupsCache } from "@/lib/docu-list-rule/code-groups/validation"; -import { InformationButton } from "@/components/information/information-button"; - -interface IndexPageProps { - searchParams: Promise<SearchParams>; -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams; - const search = searchParamsCodeGroupsCache.parse(searchParams); - - const promises = Promise.all([ - getCodeGroups({ - ...search, - }), - ]); - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <div className="flex items-center gap-2"> - <h2 className="text-2xl font-bold tracking-tight">Code Group 정의</h2> - <InformationButton pagePath="evcp/docu-list-rule/code-groups" /> - </div> - {/* <p className="text-muted-foreground"> - 문서 번호에 사용될 수 있는 다양한 코드 그룹의 정의를 관리하는 페이지입니다. - </p> */} - </div> - </div> - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}></React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={7} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["8rem", "12rem", "10rem", "10rem", "12rem", "8rem", "12rem"]} - shrinkZero - /> - } - > - <CodeGroupsTable promises={promises} /> - </React.Suspense> - </Shell> - ); -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/docu-list-rule/combo-box-settings/page.tsx b/app/[lng]/engineering/(engineering)/docu-list-rule/combo-box-settings/page.tsx deleted file mode 100644 index cf0bf02e..00000000 --- a/app/[lng]/engineering/(engineering)/docu-list-rule/combo-box-settings/page.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import * as React from "react"; -import { Shell } from "@/components/shell"; -import { Skeleton } from "@/components/ui/skeleton"; -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"; -import { getComboBoxCodeGroups } from "@/lib/docu-list-rule/combo-box-settings/service"; -import { ComboBoxSettingsTable } from "@/lib/docu-list-rule/combo-box-settings/table/combo-box-settings-table"; -import { InformationButton } from "@/components/information/information-button"; -import { searchParamsCodeGroupsCache } from "@/lib/docu-list-rule/code-groups/validation"; - -interface IndexPageProps { - searchParams: Promise<any>; -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams; - - const promises = Promise.all([ - getComboBoxCodeGroups( - searchParamsCodeGroupsCache.parse(searchParams) - ), - ]); - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <div className="flex items-center gap-2"> - <h2 className="text-2xl font-bold tracking-tight">Combo Box 설정</h2> - <InformationButton pagePath="evcp/docu-list-rule/combo-box-settings" /> - </div> - {/* <p className="text-muted-foreground"> - Combo Box 옵션을 관리하는 페이지입니다. - 각 Code Group별로 Combo Box에 표시될 옵션들을 설정할 수 있습니다. - </p> */} - </div> - </div> - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}></React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={6} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["8rem", "12rem", "10rem", "8rem", "12rem", "8rem"]} - shrinkZero - /> - } - > - <ComboBoxSettingsTable promises={promises} /> - </React.Suspense> - </Shell> - ); -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/docu-list-rule/document-class/page.tsx b/app/[lng]/engineering/(engineering)/docu-list-rule/document-class/page.tsx deleted file mode 100644 index 5c2c600e..00000000 --- a/app/[lng]/engineering/(engineering)/docu-list-rule/document-class/page.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from "react"; -import { Shell } from "@/components/shell"; -import { Skeleton } from "@/components/ui/skeleton"; -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"; -import { getDocumentClassCodeGroups } from "@/lib/docu-list-rule/document-class/service"; -import { DocumentClassTable } from "@/lib/docu-list-rule/document-class/table/document-class-table"; -import { InformationButton } from "@/components/information/information-button"; -import { searchParamsDocumentClassCache } from "@/lib/docu-list-rule/document-class/validation"; - -interface IndexPageProps { - searchParams: Promise<any>; -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams; - - const promises = Promise.all([ - getDocumentClassCodeGroups( - searchParamsDocumentClassCache.parse(searchParams) - ), - ]); - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <div className="flex items-center gap-2"> - <h2 className="text-2xl font-bold tracking-tight">Document Class 관리</h2> - <InformationButton pagePath="evcp/docu-list-rule/document-class" /> - </div> - {/* <p className="text-muted-foreground"> - Document Class를 관리합니다. - </p> */} - </div> - </div> - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}></React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={4} - searchableColumnCount={1} - filterableColumnCount={1} - cellWidths={["10rem", "20rem", "10rem", "8rem"]} - shrinkZero - /> - } - > - <DocumentClassTable promises={promises} /> - </React.Suspense> - </Shell> - ); -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/docu-list-rule/layout.tsx b/app/[lng]/engineering/(engineering)/docu-list-rule/layout.tsx deleted file mode 100644 index 25023e4b..00000000 --- a/app/[lng]/engineering/(engineering)/docu-list-rule/layout.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Metadata } from "next" - -import { Separator } from "@/components/ui/separator" -import { SidebarNav } from "@/components/layout/sidebar-nav" - -export const metadata: Metadata = { - title: "Document Numbering Rule", -} - - - -export default async function DocumentNumberingLayout({ - children, - params, -}: { - children: React.ReactNode - params: { lng: string } -}) { - const resolvedParams = await params - const lng = resolvedParams.lng - - const sidebarNavItems = [ - { - title: "Document Class 관리", - href: `/${lng}/engineering/docu-list-rule/document-class`, - }, - { - title: "Code Group 정의", - href: `/${lng}/engineering/docu-list-rule/code-groups`, - }, - { - title: "Combo Box 설정", - href: `/${lng}/engineering/docu-list-rule/combo-box-settings`, - }, - { - title: "Number Type 관리", - href: `/${lng}/engineering/docu-list-rule/number-types`, - }, - { - title: "Number Type별 설정", - href: `/${lng}/engineering/docu-list-rule/number-type-configs`, - }, - ] - - return ( - <> - <div className="container py-6"> - <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow"> - <div className="hidden space-y-6 p-10 pb-16 md:block"> - <div className="space-y-0.5"> - <h2 className="text-2xl font-bold tracking-tight">Document Numbering Rule (해양)</h2> - <p className="text-muted-foreground"> - 벤더 제출 문서 리스트 작성 시에 사용되는 넘버링 - </p> - </div> - - <Separator className="my-6" /> - <div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0"> - <aside className="-mx-4 lg:w-1/5"> - <SidebarNav items={sidebarNavItems} /> - </aside> - <div className="flex-1 ">{children}</div> - </div> - </div> - </section> - </div> - </> - ) -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/docu-list-rule/number-type-configs/page.tsx b/app/[lng]/engineering/(engineering)/docu-list-rule/number-type-configs/page.tsx deleted file mode 100644 index 4195ba24..00000000 --- a/app/[lng]/engineering/(engineering)/docu-list-rule/number-type-configs/page.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import * as React from "react"; -import { Shell } from "@/components/shell"; -import { Skeleton } from "@/components/ui/skeleton"; -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"; -import { NumberTypeConfigsTable } from "@/lib/docu-list-rule/number-type-configs/table/number-type-configs-table"; -import { getNumberTypes } from "@/lib/docu-list-rule/number-types/service"; -import { InformationButton } from "@/components/information/information-button"; - -interface IndexPageProps { - searchParams: Promise<any>; -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams; - - const promises = Promise.all([ - getNumberTypes({ - page: 1, - perPage: 1000, // 모든 Number Type을 가져오기 위해 큰 값 설정 - search: "", - sort: [{ id: "id", desc: false }], // DB 등록 순서대로 정렬 - filters: [], - joinOperator: "and", - flags: ["advancedTable"], - numberTypeId: "", - description: "", - isActive: "" - }), - ]); - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <div className="flex items-center gap-2"> - <h2 className="text-2xl font-bold tracking-tight">Number Type별 설정</h2> - <InformationButton pagePath="evcp/docu-list-rule/number-type-configs" /> - </div> - {/* <p className="text-muted-foreground"> - 각 문서 번호 유형별로 어떤 코드 그룹들을 어떤 순서로 사용할지 설정하는 페이지입니다. - </p> */} - </div> - </div> - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}></React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={6} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["10rem", "12rem", "12rem", "12rem"]} - shrinkZero - /> - } - > - <NumberTypeConfigsTable promises={promises} /> - </React.Suspense> - </Shell> - ); -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/docu-list-rule/number-types/page.tsx b/app/[lng]/engineering/(engineering)/docu-list-rule/number-types/page.tsx deleted file mode 100644 index 6fa010c7..00000000 --- a/app/[lng]/engineering/(engineering)/docu-list-rule/number-types/page.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from "react"; -import { Shell } from "@/components/shell"; -import { Skeleton } from "@/components/ui/skeleton"; -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"; -import { NumberTypesTable } from "@/lib/docu-list-rule/number-types/table/number-types-table"; -import { getNumberTypes } from "@/lib/docu-list-rule/number-types/service"; -import { InformationButton } from "@/components/information/information-button"; -import { searchParamsNumberTypesCache } from "@/lib/docu-list-rule/number-types/validation"; - -interface IndexPageProps { - searchParams: Promise<any>; -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams; - - const promises = Promise.all([ - getNumberTypes( - searchParamsNumberTypesCache.parse(searchParams) - ), - ]); - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <div className="flex items-center gap-2"> - <h2 className="text-2xl font-bold tracking-tight">Number Type 관리</h2> - <InformationButton pagePath="evcp/docu-list-rule/number-types" /> - </div> - {/* <p className="text-muted-foreground"> - 문서 번호 유형을 추가, 수정, 삭제할 수 있는 페이지입니다. - </p> */} - </div> - </div> - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}></React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={4} - searchableColumnCount={1} - filterableColumnCount={0} - cellWidths={["10rem", "20rem", "10rem", "8rem"]} - shrinkZero - /> - } - > - <NumberTypesTable promises={promises} /> - </React.Suspense> - </Shell> - ); -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/docu-list-rule/page.tsx b/app/[lng]/engineering/(engineering)/docu-list-rule/page.tsx deleted file mode 100644 index b8d3559f..00000000 --- a/app/[lng]/engineering/(engineering)/docu-list-rule/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { redirect } from "next/navigation" - - -export default async function DocumentNumberingPage({ - params, -}: { - params: Promise<{ lng: string }> -}) { - const resolvedParams = await params; - // Code Group 페이지로 리다이렉트 - redirect(`/${resolvedParams.lng}/engineering/docu-list-rule/document-class`) -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/document-list-only/layout.tsx b/app/[lng]/engineering/(engineering)/document-list-only/layout.tsx deleted file mode 100644 index 17e78c0a..00000000 --- a/app/[lng]/engineering/(engineering)/document-list-only/layout.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Shell } from "@/components/shell" -import VendorDocumentListClientEvcp from "@/components/document-lists/vendor-doc-list-client-evcp" - -// Layout 컴포넌트는 서버 컴포넌트입니다 -export default async function EvcpDocuments({ - children, -}: { - children: React.ReactNode -}) { - return ( - <Shell className="gap-2"> - <VendorDocumentListClientEvcp> - {children} - </VendorDocumentListClientEvcp> - </Shell> - ) -} diff --git a/app/[lng]/engineering/(engineering)/document-list-only/page.tsx b/app/[lng]/engineering/(engineering)/document-list-only/page.tsx deleted file mode 100644 index 5b49a6ef..00000000 --- a/app/[lng]/engineering/(engineering)/document-list-only/page.tsx +++ /dev/null @@ -1,98 +0,0 @@ -// evcp/document-list-only/page.tsx - 전체 계약 대상 문서 목록 -import * as React from "react" -import { Suspense } from "react" -import { Skeleton } from "@/components/ui/skeleton" -import { type SearchParams } from "@/types/table" -import { getValidFilters } from "@/lib/data-table" -import { DocumentStagesTable } from "@/lib/vendor-document-list/plant/document-stages-table" -import { documentStageSearchParamsCache } from "@/lib/vendor-document-list/plant/document-stage-validations" -import { getDocumentStagesOnly } from "@/lib/vendor-document-list/plant/document-stages-service" - -interface IndexPageProps { - searchParams: Promise<SearchParams> -} - -// 문서 테이블 래퍼 컴포넌트 (전체 계약용) -async function DocumentTableWrapper({ - searchParams -}: { - searchParams: SearchParams -}) { - const search = documentStageSearchParamsCache.parse(searchParams) - const validFilters = getValidFilters(search.filters) - - // 필터 타입 변환 - const convertedFilters = validFilters.map(filter => ({ - id: (filter.id || filter.rowId) as string, - value: filter.value, - operator: (filter.operator === 'iLike' ? 'ilike' : - filter.operator === 'notILike' ? 'notin' : - filter.operator === 'isEmpty' ? 'eq' : - filter.operator === 'isNotEmpty' ? 'ne' : - filter.operator === 'isBetween' ? 'eq' : - filter.operator === 'isRelativeToToday' ? 'eq' : - filter.operator || 'eq') as 'eq' | 'in' | 'ne' | 'lt' | 'lte' | 'gt' | 'gte' | 'like' | 'ilike' | 'notin' - })) - - // evcp: 전체 계약 대상으로 문서 조회 - const documentsPromise = getDocumentStagesOnly({ - ...search, - filters: convertedFilters, - }, -1) // 세션에서 자동으로 도메인 감지 - - return ( - <DocumentStagesTable - promises={Promise.all([documentsPromise])} - contractId={-1} // 전체 계약을 의미 - projectType="plant" // 기본값으로 plant 사용 - /> - ) -} - -function TableLoadingSkeleton() { - return ( - <div className="space-y-4"> - <div className="flex items-center justify-between"> - <Skeleton className="h-6 w-32" /> - <div className="flex items-center gap-2"> - <Skeleton className="h-8 w-20" /> - <Skeleton className="h-8 w-24" /> - </div> - </div> - <div className="rounded-md border"> - <div className="p-4"> - <div className="space-y-3"> - {Array.from({ length: 5 }).map((_, i) => ( - <div key={i} className="flex items-center space-x-4"> - <Skeleton className="h-4 w-4" /> - <Skeleton className="h-4 w-24" /> - <Skeleton className="h-4 w-48" /> - <Skeleton className="h-4 w-20" /> - <Skeleton className="h-4 w-16" /> - <Skeleton className="h-4 w-12" /> - </div> - ))} - </div> - </div> - </div> - </div> - ) -} - -// 메인 페이지 컴포넌트 -export default async function DocumentStagesManagementPage({ - searchParams -}: IndexPageProps) { - const resolvedSearchParams = await searchParams - - return ( - <div className="mx-auto"> - {/* 문서 테이블 */} - <Suspense fallback={<TableLoadingSkeleton />}> - <DocumentTableWrapper - searchParams={resolvedSearchParams} - /> - </Suspense> - </div> - ) -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/document-list-ship/page.tsx b/app/[lng]/engineering/(engineering)/document-list-ship/page.tsx deleted file mode 100644 index e3915419..00000000 --- a/app/[lng]/engineering/(engineering)/document-list-ship/page.tsx +++ /dev/null @@ -1,144 +0,0 @@ -// page.tsx (간단한 Promise 생성과 로그인 처리) -import * as React from "react" -import { type SearchParams } from "@/types/table" -import { getValidFilters } from "@/lib/data-table" -import { Skeleton } from "@/components/ui/skeleton" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" -import { Shell } from "@/components/shell" -import { searchParamsShipDocuCache } from "@/lib/vendor-document-list/validations" -import { getServerSession } from "next-auth" -import { authOptions } from "@/app/api/auth/[...nextauth]/route" -import Link from "next/link" -import { Button } from "@/components/ui/button" -import { LogIn } from "lucide-react" -import { getUserVendorDocumentStats, getUserVendorDocumentStatsAll, getUserVendorDocuments, getUserVendorDocumentsAll } from "@/lib/vendor-document-list/enhanced-document-service" -import { UserVendorDocumentDisplay } from "@/components/ship-vendor-document/user-vendor-document-table-container" -import { InformationButton } from "@/components/information/information-button" -import { UserVendorALLDocumentDisplay } from "@/components/ship-vendor-document-all/user-vendor-document-table-container" -interface IndexPageProps { - searchParams: Promise<SearchParams> -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams - const search = searchParamsShipDocuCache.parse(searchParams) - const validFilters = getValidFilters(search.filters) - - // Get session - const session = await getServerSession(authOptions) - - // Check if user is logged in - if (!session || !session.user) { - return ( - <Shell className="gap-6"> - <div className="flex items-center justify-between"> - <div> - <div className="flex items-center gap-2"> - <h2 className="text-2xl font-bold tracking-tight"> - 문서 관리 - </h2> - - </div> - {/* <p className="text-muted-foreground"> - 소속 회사의 모든 도서/도면을 확인하고 관리합니다. - </p> */} - </div> - </div> - - <div className="flex flex-col items-center justify-center py-12 text-center"> - <div className="rounded-lg border border-dashed p-10 shadow-sm"> - <h3 className="mb-2 text-xl font-semibold">로그인이 필요합니다</h3> - <p className="mb-6 text-muted-foreground"> - 문서를 확인하려면 먼저 로그인하세요. - </p> - <Button size="lg" asChild> - <Link href="/partners"> - <LogIn className="mr-2 h-4 w-4" /> - 로그인하기 - </Link> - </Button> - </div> - </div> - </Shell> - ) - } - - // User is logged in, get user ID - const requesterId = session.user.id ? Number(session.user.id) : null - - if (!requesterId) { - return ( - <Shell className="gap-6"> - <div className="flex items-center justify-between"> - <div> - <h2 className="text-2xl font-bold tracking-tight"> - Document Management - </h2> - </div> - </div> - <div className="flex flex-col items-center justify-center py-12 text-center"> - <div className="rounded-lg border border-dashed p-10 shadow-sm"> - <h3 className="mb-2 text-xl font-semibold">계정 오류</h3> - <p className="mb-6 text-muted-foreground"> - 사용자 정보가 올바르게 설정되지 않았습니다. 관리자에게 문의하세요. - </p> - </div> - </div> - </Shell> - ) - } - - // 검색 파라미터 정리 - const searchInput = { - ...search, - filters: validFilters, - } - - // Promise 생성 (모든 데이터를 페이지에서 처리) - const documentsPromise = getUserVendorDocumentsAll(requesterId, searchInput) - const statsPromise = getUserVendorDocumentStatsAll(requesterId) - - // Promise.all로 감싸서 전달 - const allPromises = Promise.all([documentsPromise, statsPromise]) - const statsResult = await documentsPromise - - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <div className="flex items-center gap-2"> - <h2 className="text-2xl font-bold tracking-tight"> - 조선 Document Management - </h2> - <InformationButton pagePath="evcp/document-list-ship" /> - </div> - <p className="text-muted-foreground"> - - </p> - </div> - </div> - - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}> - {/* DateRangePicker can go here */} - </React.Suspense> - - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={8} - searchableColumnCount={1} - filterableColumnCount={3} - cellWidths={["10rem", "30rem", "15rem", "15rem", "15rem", "15rem", "8rem", "8rem"]} - shrinkZero - /> - } - > - <UserVendorALLDocumentDisplay - allPromises={allPromises} - /> - </React.Suspense> - </Shell> - ) -} - diff --git a/app/[lng]/engineering/(engineering)/faq/manage/actions.ts b/app/[lng]/engineering/(engineering)/faq/manage/actions.ts deleted file mode 100644 index bc443a8a..00000000 --- a/app/[lng]/engineering/(engineering)/faq/manage/actions.ts +++ /dev/null @@ -1,48 +0,0 @@ -'use server';
-
-import { promises as fs } from 'fs';
-import path from 'path';
-import { FaqCategory } from '@/components/faq/FaqCard';
-import { fallbackLng } from '@/i18n/settings';
-
-const FAQ_CONFIG_PATH = path.join(process.cwd(), 'config', 'faqDataConfig.ts');
-
-export async function updateFaqData(lng: string, newData: FaqCategory[]) {
- try {
- const fileContent = await fs.readFile(FAQ_CONFIG_PATH, 'utf-8');
- const dataMatch = fileContent.match(/export const faqCategories[^=]*=\s*(\{[\s\S]*\});/);
- if (!dataMatch) {
- throw new Error('FAQ 데이터 형식이 올바르지 않습니다.');
- }
-
- const allData = eval(`(${dataMatch[1]})`);
- const updatedData = {
- ...allData,
- [lng]: newData
- };
-
- const newFileContent = `import { FaqCategory } from "@/components/faq/FaqCard";\n\ninterface LocalizedFaqCategories {\n [lng: string]: FaqCategory[];\n}\n\nexport const faqCategories: LocalizedFaqCategories = ${JSON.stringify(updatedData, null, 4)};`;
- await fs.writeFile(FAQ_CONFIG_PATH, newFileContent, 'utf-8');
-
- return { success: true };
- } catch (error) {
- console.error('FAQ 데이터 업데이트 중 오류 발생:', error);
- return { success: false, error: '데이터 업데이트 중 오류가 발생했습니다.' };
- }
-}
-
-export async function getFaqData(lng: string): Promise<{ data: FaqCategory[] }> {
- try {
- const fileContent = await fs.readFile(FAQ_CONFIG_PATH, 'utf-8');
- const dataMatch = fileContent.match(/export const faqCategories[^=]*=\s*(\{[\s\S]*\});/);
- if (!dataMatch) {
- throw new Error('FAQ 데이터 형식이 올바르지 않습니다.');
- }
-
- const allData = eval(`(${dataMatch[1]})`);
- return { data: allData[lng] || allData[fallbackLng] || [] };
- } catch (error) {
- console.error('FAQ 데이터 읽기 중 오류 발생:', error);
- return { data: [] };
- }
-}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/faq/manage/page.tsx b/app/[lng]/engineering/(engineering)/faq/manage/page.tsx deleted file mode 100644 index 011bbfa4..00000000 --- a/app/[lng]/engineering/(engineering)/faq/manage/page.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { FaqManager } from '@/components/faq/FaqManager';
-import { getFaqData, updateFaqData } from './actions';
-import { revalidatePath } from 'next/cache';
-import { FaqCategory } from '@/components/faq/FaqCard';
-
-interface Props {
- params: {
- lng: string;
- }
-}
-
-export default async function FaqManagePage(props: Props) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const { data } = await getFaqData(lng);
-
- async function handleSave(newData: FaqCategory[]) {
- 'use server';
- await updateFaqData(lng, newData);
- revalidatePath(`/${lng}/evcp/faq`);
- }
-
- return (
- <div className="container py-6">
- <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
- <div className="space-y-6 p-10 pb-16">
- <div className="space-y-0.5">
- <h2 className="text-2xl font-bold tracking-tight">FAQ Management</h2>
- <p className="text-muted-foreground">
- Manage FAQ categories and items for {lng.toUpperCase()} language.
- </p>
- </div>
- <FaqManager initialData={data} onSave={handleSave} lng={lng} />
- </div>
- </section>
- </div>
- );
-}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/faq/page.tsx b/app/[lng]/engineering/(engineering)/faq/page.tsx deleted file mode 100644 index 9b62b7e4..00000000 --- a/app/[lng]/engineering/(engineering)/faq/page.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Separator } from "@/components/ui/separator"
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
-import { faqCategories } from "@/config/faqDataConfig"
-import { FaqCard } from "@/components/faq/FaqCard"
-import { Button } from "@/components/ui/button"
-import { Settings } from "lucide-react"
-import Link from "next/link"
-import { fallbackLng } from "@/i18n/settings"
-
-interface Props {
- params: {
- lng: string;
- }
-}
-
-export default async function FaqPage(props: Props) {
- const resolvedParams = await props.params
- const lng = resolvedParams.lng
- const localizedFaqCategories = faqCategories[lng] || faqCategories[fallbackLng];
-
- return (
- <div className="container py-6">
- <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
- <div className="space-y-6 p-10 pb-16">
- <div className="flex justify-between items-center">
- <div className="space-y-0.5">
- <h2 className="text-2xl font-bold tracking-tight">Frequently Asked Questions</h2>
- <p className="text-muted-foreground">
- Find answers to common questions about using the EVCP system.
- </p>
- </div>
- <Link href={`/${lng}/evcp/faq/manage`}>
- <Button variant="outline">
- <Settings className="w-4 h-4 mr-2" />
- Manage FAQ
- </Button>
- </Link>
- </div>
- <Separator className="my-6" />
-
- <Tabs defaultValue={localizedFaqCategories[0]?.label} className="space-y-4">
- <TabsList>
- {localizedFaqCategories.map((category) => (
- <TabsTrigger key={category.label} value={category.label}>
- {category.label}
- </TabsTrigger>
- ))}
- </TabsList>
-
- {localizedFaqCategories.map((category) => (
- <TabsContent key={category.label} value={category.label} className="space-y-4">
- {category.items.map((item, index) => (
- <FaqCard key={index} item={item} />
- ))}
- </TabsContent>
- ))}
- </Tabs>
- </div>
- </section>
- </div>
- )
-}
diff --git a/app/[lng]/engineering/(engineering)/form-list/page.tsx b/app/[lng]/engineering/(engineering)/form-list/page.tsx deleted file mode 100644 index a2c6fbb9..00000000 --- a/app/[lng]/engineering/(engineering)/form-list/page.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import * as React from "react" -import { type SearchParams } from "@/types/table" - -import { getValidFilters } from "@/lib/data-table" -import { Skeleton } from "@/components/ui/skeleton" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" -import { Shell } from "@/components/shell" -import { searchParamsCache } from "@/lib/form-list/validation" -import { ItemsTable } from "@/lib/items/table/items-table" -import { getFormLists } from "@/lib/form-list/service" -import { FormListsTable } from "@/lib/form-list/table/formLists-table" - - -interface IndexPageProps { - searchParams: Promise<SearchParams> -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams - const search = searchParamsCache.parse(searchParams) - - const validFilters = getValidFilters(search.filters) - - const promises = Promise.all([ - getFormLists({ - ...search, - filters: validFilters, - }), - - ]) - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <h2 className="text-2xl font-bold tracking-tight"> - 레지스터 목록 from S-EDP - </h2> - {/* <p className="text-muted-foreground"> - 협력업체 데이터 입력을 위한 레지스터 목록 리스트입니다.{" "} - <span className="inline-flex items-center whitespace-nowrap"> - <Ellipsis className="size-3" /> - <span className="ml-1">버튼</span> - </span> - 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. - </p> */} - </div> - </div> - </div> - - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}> - {/* <DateRangePicker - triggerSize="sm" - triggerClassName="ml-auto w-56 sm:w-60" - align="end" - shallow={false} - /> */} - </React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={6} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]} - shrinkZero - /> - } - > - <FormListsTable promises={promises} /> - </React.Suspense> - </Shell> - ) -} diff --git a/app/[lng]/engineering/(engineering)/items/page.tsx b/app/[lng]/engineering/(engineering)/items/page.tsx deleted file mode 100644 index f8d9a5b1..00000000 --- a/app/[lng]/engineering/(engineering)/items/page.tsx +++ /dev/null @@ -1,68 +0,0 @@ -// app/items/page.tsx (업데이트) -import * as React from "react" -import { type SearchParams } from "@/types/table" - -import { Skeleton } from "@/components/ui/skeleton" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" -import { Shell } from "@/components/shell" -import { searchParamsCache } from "@/lib/items/validations" -import { getItems } from "@/lib/items/service" -import { ItemsTable } from "@/lib/items/table/items-table" -import { ViewModeToggle } from "@/components/data-table/view-mode-toggle" - -interface IndexPageProps { - searchParams: Promise<SearchParams> -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams - const search = searchParamsCache.parse(searchParams) - - // pageSize 기반으로 모드 자동 결정 - const isInfiniteMode = search.perPage >= 1_000_000 - - // 페이지네이션 모드일 때만 서버에서 데이터 가져오기 - // 무한 스크롤 모드에서는 클라이언트에서 SWR로 데이터 로드 - const promises = isInfiniteMode - ? undefined - : Promise.all([ - getItems(search), // searchParamsCache의 결과를 그대로 사용 - ]) - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <h2 className="text-2xl font-bold tracking-tight"> - 패키지 넘버 - </h2> - {/* <p className="text-muted-foreground"> - S-EDP로부터 수신된 패키지 정보이며 PR 전 입찰, 견적에 사용되며 벤더 데이터, 문서와 연결됩니다. - </p> */} - </div> - </div> - - </div> - - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}> - {/* DateRangePicker 등 추가 컴포넌트 */} - </React.Suspense> - - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={6} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]} - shrinkZero - /> - } - > - {/* 통합된 ItemsTable 컴포넌트 사용 */} - <ItemsTable promises={promises} /> - </React.Suspense> - </Shell> - ) -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/layout.tsx b/app/[lng]/engineering/(engineering)/layout.tsx deleted file mode 100644 index 82b53307..00000000 --- a/app/[lng]/engineering/(engineering)/layout.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { ReactNode } from 'react'; -import { Header } from '@/components/layout/Header'; -import { SiteFooter } from '@/components/layout/Footer'; - -export default function EvcpLayout({ children }: { children: ReactNode }) { - return ( - <div className="relative flex min-h-svh flex-col bg-background"> - {/* <div className="relative flex min-h-svh flex-col bg-slate-100 "> */} - <Header /> - <main className="flex flex-1 flex-col"> - <div className='container-wrapper'> - {children} - </div> - </main> - <SiteFooter/> - </div> - ); -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/projects/page.tsx b/app/[lng]/engineering/(engineering)/projects/page.tsx deleted file mode 100644 index 199b175b..00000000 --- a/app/[lng]/engineering/(engineering)/projects/page.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import * as React from "react" -import { type SearchParams } from "@/types/table" - -import { getValidFilters } from "@/lib/data-table" -import { Skeleton } from "@/components/ui/skeleton" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" -import { Shell } from "@/components/shell" -import { ItemsTable } from "@/lib/items/table/items-table" -import { getProjectLists } from "@/lib/projects/service" -import { ProjectsTable } from "@/lib/projects/table/projects-table" -import { searchParamsProjectsCache } from "@/lib/projects/validation" - - -interface IndexPageProps { - searchParams: Promise<SearchParams> -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams - const search = searchParamsProjectsCache.parse(searchParams) - - const validFilters = getValidFilters(search.filters) - - const promises = Promise.all([ - getProjectLists({ - ...search, - filters: validFilters, - }), - - ]) - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <h2 className="text-2xl font-bold tracking-tight"> - 프로젝트 리스트 from S-EDP - </h2> - {/* <p className="text-muted-foreground"> - S-EDP로부터 수신하는 프로젝트 리스트입니다. 향후 MDG로 전환됩니다.{" "} - <span className="inline-flex items-center whitespace-nowrap"> - <Ellipsis className="size-3" /> - <span className="ml-1">버튼</span> - </span> - 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. - </p> */} - </div> - </div> - </div> - - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}> - {/* <DateRangePicker - triggerSize="sm" - triggerClassName="ml-auto w-56 sm:w-60" - align="end" - shallow={false} - /> */} - </React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={6} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]} - shrinkZero - /> - } - > - <ProjectsTable promises={promises} /> - </React.Suspense> - </Shell> - ) -} diff --git a/app/[lng]/engineering/(engineering)/report/page.tsx b/app/[lng]/engineering/(engineering)/report/page.tsx deleted file mode 100644 index 64778ef1..00000000 --- a/app/[lng]/engineering/(engineering)/report/page.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import * as React from "react"; -import { Skeleton } from "@/components/ui/skeleton"; -import { Shell } from "@/components/shell"; -import { ErrorBoundary } from "@/components/error-boundary"; -import { getDashboardData } from "@/lib/dashboard/service"; -import { DashboardClient } from "@/lib/dashboard/dashboard-client"; - -// 데이터 fetch 시 비동기 함수 호출 후 await 하므로 static-pre-render 과정에서 dynamic-server-error 발생. -// 따라서, dynamic 속성을 force-dynamic 으로 설정하여 동적 렌더링 처리 -// getDashboardData 함수에 대한 Promise를 넘기는 식으로 수정하게 되면 force-dynamic 선언을 제거해도 됨. -export const dynamic = 'force-dynamic' - -export default async function IndexPage() { - // domain을 명시적으로 전달 - const domain = "engineering"; - - try { - // 서버에서 직접 데이터 fetch - const dashboardData = await getDashboardData(domain); - - return ( - <Shell className="gap-2"> - <DashboardClient initialData={dashboardData} /> - </Shell> - ); - } catch (error) { - console.error("Dashboard data fetch error:", error); - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-center py-12"> - <div className="text-center space-y-2"> - <p className="text-destructive">데이터를 불러오는데 실패했습니다.</p> - <p className="text-muted-foreground text-sm">{error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다."}</p> - </div> - </div> - </Shell> - ); - } -} - -function DashboardSkeleton() { - return ( - <div className="space-y-6"> - {/* 헤더 스켈레톤 */} - <div className="flex items-center justify-between"> - <div className="space-y-2"> - <Skeleton className="h-8 w-48" /> - <Skeleton className="h-4 w-72" /> - </div> - <Skeleton className="h-10 w-24" /> - </div> - - {/* 요약 카드 스켈레톤 */} - <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4"> - {[...Array(4)].map((_, i) => ( - <div key={i} className="space-y-3 p-6 border rounded-lg"> - <div className="flex items-center justify-between"> - <Skeleton className="h-4 w-16" /> - <Skeleton className="h-4 w-4" /> - </div> - <Skeleton className="h-8 w-12" /> - <Skeleton className="h-3 w-20" /> - </div> - ))} - </div> - - {/* 차트 스켈레톤 */} - <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> - {[...Array(2)].map((_, i) => ( - <div key={i} className="space-y-4 p-6 border rounded-lg"> - <div className="space-y-2"> - <Skeleton className="h-6 w-32" /> - <Skeleton className="h-4 w-48" /> - </div> - <Skeleton className="h-[300px] w-full" /> - </div> - ))} - </div> - - {/* 탭 스켈레톤 */} - <div className="space-y-4"> - <Skeleton className="h-10 w-64" /> - <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3"> - {[...Array(6)].map((_, i) => ( - <div key={i} className="space-y-4 p-6 border rounded-lg"> - <Skeleton className="h-6 w-32" /> - <div className="space-y-3"> - <div className="flex justify-between"> - <Skeleton className="h-4 w-16" /> - <Skeleton className="h-4 w-12" /> - </div> - <div className="flex gap-2"> - <Skeleton className="h-6 w-16" /> - <Skeleton className="h-6 w-16" /> - <Skeleton className="h-6 w-16" /> - </div> - <Skeleton className="h-2 w-full" /> - </div> - </div> - ))} - </div> - </div> - </div> - ); -} diff --git a/app/[lng]/engineering/(engineering)/tag-numbering/page.tsx b/app/[lng]/engineering/(engineering)/tag-numbering/page.tsx deleted file mode 100644 index 86ad2ec2..00000000 --- a/app/[lng]/engineering/(engineering)/tag-numbering/page.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from "react" -import { type SearchParams } from "@/types/table" - -import { getValidFilters } from "@/lib/data-table" -import { Skeleton } from "@/components/ui/skeleton" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" -import { Shell } from "@/components/shell" -import { searchParamsCache } from "@/lib/tag-numbering/validation" -import { getTagNumbering } from "@/lib/tag-numbering/service" -import { TagNumberingTable } from "@/lib/tag-numbering/table/tagNumbering-table" - - -interface IndexPageProps { - searchParams: Promise<SearchParams> -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams - const search = searchParamsCache.parse(searchParams) - - const validFilters = getValidFilters(search.filters) - - const promises = Promise.all([ - getTagNumbering({ - ...search, - filters: validFilters, - }), - - ]) - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <h2 className="text-2xl font-bold tracking-tight"> - 태그 타입 목록 from S-EDP - </h2> - {/* <p className="text-muted-foreground"> - 태그 넘버링을 위한 룰셋을 S-EDP로부터 가져오고 확인할 수 있습니다{" "} - <span className="inline-flex items-center whitespace-nowrap"> - <Ellipsis className="size-3" /> - <span className="ml-1">버튼</span> - </span> - 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. - </p> */} - </div> - </div> - </div> - - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}> - {/* <DateRangePicker - triggerSize="sm" - triggerClassName="ml-auto w-56 sm:w-60" - align="end" - shallow={false} - /> */} - </React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={6} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]} - shrinkZero - /> - } - > - <TagNumberingTable promises={promises} /> - </React.Suspense> - </Shell> - ) -} diff --git a/app/[lng]/engineering/(engineering)/tasks/page.tsx b/app/[lng]/engineering/(engineering)/tasks/page.tsx deleted file mode 100644 index 91b946fb..00000000 --- a/app/[lng]/engineering/(engineering)/tasks/page.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import * as React from "react" -import { type SearchParams } from "@/types/table" - -import { getValidFilters } from "@/lib/data-table" -import { Skeleton } from "@/components/ui/skeleton" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" -import { DateRangePicker } from "@/components/date-range-picker" -import { Shell } from "@/components/shell" - -import { FeatureFlagsProvider } from "@/lib/tasks/table/feature-flags-provider" -import { TasksTable } from "@/lib/tasks/table/tasks-table" -import { - getTaskPriorityCounts, - getTasks, - getTaskStatusCounts, -} from "@/lib/tasks/service" -import { searchParamsCache } from "@/lib/tasks/validations" - -interface IndexPageProps { - searchParams: Promise<SearchParams> -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams - const search = searchParamsCache.parse(searchParams) - - const validFilters = getValidFilters(search.filters) - - const promises = Promise.all([ - getTasks({ - ...search, - filters: validFilters, - }), - getTaskStatusCounts(), - getTaskPriorityCounts(), - ]) - - return ( - <Shell className="gap-2"> - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}> - <DateRangePicker - triggerSize="sm" - triggerClassName="ml-auto w-56 sm:w-60" - align="end" - shallow={false} - /> - </React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={6} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]} - shrinkZero - /> - } - > - <TasksTable promises={promises} /> - </React.Suspense> - </Shell> - ) -} diff --git a/app/[lng]/engineering/(engineering)/tbe/page.tsx b/app/[lng]/engineering/(engineering)/tbe/page.tsx deleted file mode 100644 index 211cf376..00000000 --- a/app/[lng]/engineering/(engineering)/tbe/page.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { type SearchParams } from "@/types/table" -import { getValidFilters } from "@/lib/data-table" -import { getAllTBE } from "@/lib/rfqs/service" -import { searchParamsTBECache } from "@/lib/rfqs/validations" -import { AllTbeTable } from "@/lib/tbe/table/tbe-table" -import { RfqType } from "@/lib/rfqs/validations" -import * as React from "react" -import { Shell } from "@/components/shell" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" -import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs" - -interface IndexPageProps { - params: { - lng: string - } - searchParams: Promise<SearchParams> -} - -// 타입별 페이지 설명 구성 (Budgetary 제외) -const typeConfig: Record<string, { title: string; description: string; rfqType: RfqType }> = { - "purchase": { - title: "Purchase RFQ Technical Bid Evaluation", - description: "실제 구매 발주 전 가격 요청을 위한 TBE입니다.", - rfqType: RfqType.PURCHASE - }, - "purchase-budgetary": { - title: "Purchase Budgetary RFQ Technical Bid Evaluation", - description: "프로젝트 수주 후, 공식 입찰 전 예산 책정을 위한 TBE입니다.", - rfqType: RfqType.PURCHASE_BUDGETARY - } -} - -export default async function RfqTBEPage(props: IndexPageProps) { - const resolvedParams = await props.params - const lng = resolvedParams.lng - - // URL 쿼리 파라미터에서 타입 추출 - const searchParams = await props.searchParams - // 기본값으로 'purchase' 사용 - const typeParam = searchParams?.type as string || 'purchase' - - // 유효한 타입인지 확인하고 기본값 설정 - const validType = Object.keys(typeConfig).includes(typeParam) ? typeParam : 'purchase' - const rfqType = typeConfig[validType].rfqType - - // SearchParams 파싱 (Zod) - const search = searchParamsTBECache.parse(searchParams) - const validFilters = getValidFilters(search.filters) - - // 현재 선택된 타입의 데이터 로드 - const promises = Promise.all([ - getAllTBE({ - ...search, - filters: validFilters, - rfqType - }) - ]) - - // 페이지 경로 생성 함수 - 단순화 - const getTabUrl = (type: string) => { - return `/${lng}/evcp/tbe?type=${type}`; - } - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <h2 className="text-2xl font-bold tracking-tight"> - TBE 관리 - </h2> - {/* <p className="text-muted-foreground"> - 초대된 협력업체에게 TBE를 보낼 수 있습니다. <br/> - 체크박스 선택을 하면 초대 버튼이 활성화됩니다. 버튼 클릭 후 첨부파일을 함께 전송하면 TBE 내용이 메일로 전달되고 eVCP에도 협력업체가 입력할 수 있게 자동 생성됩니다. - </p> */} - </div> - </div> - </div> - - {/* 타입 선택 탭 (Budgetary 제외) */} - <Tabs defaultValue={validType} value={validType} className="w-full"> - <TabsList className="grid grid-cols-2 w-full max-w-md"> - <TabsTrigger value="purchase" asChild> - <a href={getTabUrl('purchase')}>Purchase</a> - </TabsTrigger> - <TabsTrigger value="purchase-budgetary" asChild> - <a href={getTabUrl('purchase-budgetary')}>Purchase Budgetary</a> - </TabsTrigger> - </TabsList> - - <div className="mt-2"> - <p className="text-sm text-muted-foreground"> - {typeConfig[validType].description} - </p> - </div> - </Tabs> - - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={6} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]} - shrinkZero - /> - } - > - <AllTbeTable promises={promises}/> - </React.Suspense> - </Shell> - ) -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/vendor-check-list/page.tsx b/app/[lng]/engineering/(engineering)/vendor-check-list/page.tsx deleted file mode 100644 index e6f9ce82..00000000 --- a/app/[lng]/engineering/(engineering)/vendor-check-list/page.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from "react" -import { type SearchParams } from "@/types/table" - -import { getValidFilters } from "@/lib/data-table" -import { Skeleton } from "@/components/ui/skeleton" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" -import { Shell } from "@/components/shell" -import { getGenralEvaluationsSchema } from "@/lib/general-check-list/validation" -import { GeneralEvaluationsTable } from "@/lib/general-check-list/table/general-check-list-table" -import { getGeneralEvaluations } from "@/lib/general-check-list/service" - - -interface IndexPageProps { - searchParams: Promise<SearchParams> -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams - const search = getGenralEvaluationsSchema.parse(searchParams) - - const validFilters = getValidFilters(search.filters) - - const promises = Promise.all([ - getGeneralEvaluations({ - ...search, - filters: validFilters, - }), - - ]) - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <h2 className="text-2xl font-bold tracking-tight"> - 협력업체 평가자료 문항 관리 - </h2> - {/* <p className="text-muted-foreground"> - 협력업체 평가에 사용되는 정기평가 체크리스트를 관리{" "} - <span className="inline-flex items-center whitespace-nowrap"> - <Ellipsis className="size-3" /> - <span className="ml-1">버튼</span> - </span> - 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다. - </p> */} - </div> - </div> - </div> - - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}> - {/* <DateRangePicker - triggerSize="sm" - triggerClassName="ml-auto w-56 sm:w-60" - align="end" - shallow={false} - /> */} - </React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={6} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]} - shrinkZero - /> - } - > - <GeneralEvaluationsTable promises={promises} /> - </React.Suspense> - </Shell> - ) -} diff --git a/app/[lng]/engineering/(engineering)/vendor-data/form/[packageId]/[formId]/[projectId]/[contractId]/page.tsx b/app/[lng]/engineering/(engineering)/vendor-data/form/[packageId]/[formId]/[projectId]/[contractId]/page.tsx deleted file mode 100644 index f69aa525..00000000 --- a/app/[lng]/engineering/(engineering)/vendor-data/form/[packageId]/[formId]/[projectId]/[contractId]/page.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import DynamicTable from "@/components/form-data/form-data-table"; -import { findContractItemId, getFormData, getFormId } from "@/lib/forms/services"; - -interface IndexPageProps { - params: { - lng: string; - packageId: string; - formId: string; - projectId: string; - contractId: string; - - - }; - searchParams?: { - mode?: string; - }; -} - -export default async function FormPage({ params, searchParams }: IndexPageProps) { - // 1) 구조 분해 할당 - const resolvedParams = await params; - - // 2) searchParams도 await 필요 - const resolvedSearchParams = await searchParams; - - // 3) 구조 분해 할당 - const { lng, packageId, formId: formCode, projectId,contractId } = resolvedParams; - - // URL 쿼리 파라미터에서 mode 가져오기 (await 해서 사용) - const mode = resolvedSearchParams?.mode === "ENG" ? "ENG" : "IM"; // 기본값은 IM - - // 4) 변환 - let packageIdAsNumber = Number(packageId); - const contractIdAsNumber = Number(contractId); - - // packageId가 0이면 contractId와 formCode로 실제 contractItemId 찾기 - if (packageIdAsNumber === 0 && contractIdAsNumber > 0) { - console.log(`packageId가 0이므로 contractId ${contractIdAsNumber}와 formCode ${formCode}로 contractItemId 조회`); - - const foundContractItemId = await findContractItemId(contractIdAsNumber, formCode); - - if (foundContractItemId) { - console.log(`contractItemId ${foundContractItemId}를 찾았습니다. 이 값을 사용합니다.`); - packageIdAsNumber = foundContractItemId; - } else { - console.warn(`contractItemId를 찾을 수 없습니다. packageId는 계속 0으로 유지됩니다.`); - } - } - - // 5) DB 조회 - const { columns, data, editableFieldsMap } = await getFormData(formCode, packageIdAsNumber); - - - // 6) formId 및 report temp file 조회 - const { formId } = await getFormId(String(packageIdAsNumber), formCode); - - // 7) 예외 처리 - if (!columns) { - return ( - <p className="text-red-500">해당 폼의 메타 정보를 불러올 수 없습니다. ENG 모드의 경우에는 SHI 관리자에게 폼 생성 요청을 하시기 바랍니다.</p> - ); - } - - // 8) 렌더링 - return ( - <div className="space-y-6"> - <DynamicTable - contractItemId={packageIdAsNumber} - formCode={formCode} - formId={formId} - columnsJSON={columns} - dataJSON={data} - projectId={Number(projectId)} - editableFieldsMap={editableFieldsMap} // 새로 추가 - mode={mode} // 모드 전달 - /> - </div> - ); -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/vendor-data/layout.tsx b/app/[lng]/engineering/(engineering)/vendor-data/layout.tsx deleted file mode 100644 index 7d00359c..00000000 --- a/app/[lng]/engineering/(engineering)/vendor-data/layout.tsx +++ /dev/null @@ -1,67 +0,0 @@ -// app/vendor-data/layout.tsx -import * as React from "react" -import { cookies } from "next/headers" -import { Shell } from "@/components/shell" -import { getVendorProjectsAndContracts } from "@/lib/vendor-data/services" -import { VendorDataContainer } from "@/components/vendor-data/vendor-data-container" -import { InformationButton } from "@/components/information/information-button" -// Layout 컴포넌트는 서버 컴포넌트입니다 -export default async function VendorDataLayout({ - children, -}: { - children: React.ReactNode -}) { - // evcp: 전체 계약 대상으로 프로젝트 데이터 가져오기 - const projects = await getVendorProjectsAndContracts() - - // 레이아웃 설정 쿠키 가져오기 - // Next.js 15에서는 cookies()가 Promise를 반환하므로 await 사용 - const cookieStore = await cookies() - - // 이제 cookieStore.get() 메서드 사용 가능 - const layout = cookieStore.get("react-resizable-panels:layout:mail") - const collapsed = cookieStore.get("react-resizable-panels:collapsed") - - const defaultLayout = layout ? JSON.parse(layout.value) : undefined - const defaultCollapsed = collapsed ? JSON.parse(collapsed.value) : undefined - - return ( - <Shell className="gap-2"> - <div className="flex items-center justify-between space-y-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <div className="flex items-center gap-2"> - <h2 className="text-2xl font-bold tracking-tight"> - 협력업체 데이터 입력 - </h2> - <InformationButton pagePath="partners/vendor-data" /> - </div> - {/* <p className="text-muted-foreground"> - 각종 Data 입력할 수 있습니다 - </p> */} - </div> - </div> - </div> - - <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow"> - <div className="hidden flex-col md:flex"> - {projects.length === 0 ? ( - <div className="p-4 text-center text-sm text-muted-foreground"> - No projects found for this vendor. - </div> - ) : ( - <VendorDataContainer - projects={projects} - defaultLayout={defaultLayout} - defaultCollapsed={defaultCollapsed} - navCollapsedSize={4} - > - {/* 페이지별 콘텐츠가 여기에 들어갑니다 */} - {children} - </VendorDataContainer> - )} - </div> - </section> - </Shell> - ) -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/vendor-data/page.tsx b/app/[lng]/engineering/(engineering)/vendor-data/page.tsx deleted file mode 100644 index ddc21a2b..00000000 --- a/app/[lng]/engineering/(engineering)/vendor-data/page.tsx +++ /dev/null @@ -1,28 +0,0 @@ -// evcp/vendor-data/page.tsx - 전체 계약 대상 협력업체 데이터 -import * as React from "react" -import { Separator } from "@/components/ui/separator" - -export default async function IndexPage() { - return ( - <div className="space-y-6"> - <div> - <h3 className="text-lg font-medium">전체 계약 협력업체 데이터 대시보드</h3> - <p className="text-sm text-muted-foreground"> - 모든 계약의 협력업체 데이터를 확인하고 관리할 수 있습니다. - </p> - </div> - <Separator /> - <div className="grid gap-4"> - <div className="rounded-lg border p-4"> - <h4 className="text-sm font-medium">사용 방법</h4> - <p className="text-sm text-muted-foreground mt-1"> - 1. 왼쪽 사이드바에서 계약을 선택하세요.<br /> - 2. 선택한 계약의 패키지 항목을 클릭하세요.<br /> - 3. 패키지의 태그 정보를 확인하고 관리할 수 있습니다.<br /> - 4. 폼 항목을 클릭하여 칼럼 정보를 확인하고 관리할 수 있습니다. - </p> - </div> - </div> - </div> - ) -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/vendor-data/tag/[id]/page.tsx b/app/[lng]/engineering/(engineering)/vendor-data/tag/[id]/page.tsx deleted file mode 100644 index 7250732f..00000000 --- a/app/[lng]/engineering/(engineering)/vendor-data/tag/[id]/page.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Separator } from "@/components/ui/separator" -import { type SearchParams } from "@/types/table" -import { getValidFilters } from "@/lib/data-table" -import { TagsTable } from "@/lib/tags/table/tag-table" -import { searchParamsCache } from "@/lib/tags/validations" -import { getTags } from "@/lib/tags/service" - -interface IndexPageProps { - params: { - id: string - } - searchParams: Promise<SearchParams> -} - -export default async function TagPage(props: IndexPageProps) { - const resolvedParams = await props.params - const id = resolvedParams.id - - const idAsNumber = Number(id) - - // 2) SearchParams 파싱 (Zod) - // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼 - const searchParams = await props.searchParams - const search = searchParamsCache.parse(searchParams) - const validFilters = getValidFilters(search.filters) - - const promises = Promise.all([ - getTags({ - ...search, - filters: validFilters, - }, - idAsNumber) - ]) - - // 4) 렌더링 - return ( - <div className="space-y-6"> - <div> - <TagsTable promises={promises} selectedPackageId={idAsNumber}/> - </div> - </div> - ) -}
\ No newline at end of file diff --git a/app/[lng]/engineering/(engineering)/vendor-investigation/page.tsx b/app/[lng]/engineering/(engineering)/vendor-investigation/page.tsx deleted file mode 100644 index af9f3e11..00000000 --- a/app/[lng]/engineering/(engineering)/vendor-investigation/page.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import * as React from "react" -import { type SearchParams } from "@/types/table" - -import { getValidFilters } from "@/lib/data-table" -import { Skeleton } from "@/components/ui/skeleton" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" -import { Shell } from "@/components/shell" - -import { VendorsInvestigationTable } from "@/lib/vendor-investigation/table/investigation-table" -import { getVendorsInvestigation } from "@/lib/vendor-investigation/service" -import { searchParamsInvestigationCache } from "@/lib/vendor-investigation/validations" - -interface IndexPageProps { - searchParams: Promise<SearchParams> -} - -export default async function IndexPage(props: IndexPageProps) { - const searchParams = await props.searchParams - const search = searchParamsInvestigationCache.parse(searchParams) - - const validFilters = getValidFilters(search.filters) - - const promises = Promise.all([ - getVendorsInvestigation({ - ...search, - filters: validFilters, - }), - ]) - - return ( - <Shell className="gap-2"> - - <div className="flex items-center justify-between space-y-2"> - <div className="flex items-center justify-between space-y-2"> - <div> - <h2 className="text-2xl font-bold tracking-tight"> - 협력업체 실사 관리 - </h2> - {/* <p className="text-muted-foreground"> - 요청된 Vendor 실사에 대한 스케줄 정보를 관리하고 결과를 입력할 수 있습니다. - - </p> */} - </div> - </div> - </div> - - - <React.Suspense fallback={<Skeleton className="h-7 w-52" />}> - </React.Suspense> - <React.Suspense - fallback={ - <DataTableSkeleton - columnCount={6} - searchableColumnCount={1} - filterableColumnCount={2} - cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]} - shrinkZero - /> - } - > - <VendorsInvestigationTable promises={promises}/> - </React.Suspense> - </Shell> - ) -} |
